using System;
using System.Linq;
using System.Collections.Generic;
using GameFramework;
using GameFramework.Event;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;

namespace LS.Fsm
{
    public sealed class SceneLoaderState<TOwner> : LoaderBaseState<TOwner> where TOwner : class, ISceneLoaderOwner
    {
        private bool m_TotulFinish = false;
        private readonly Dictionary<string, bool> m_LoadFinishResult = new Dictionary<string, bool>();
        private readonly Dictionary<string, bool> m_LoadStateResult = new Dictionary<string, bool>();
        private readonly Dictionary<string, float> m_LoadProgresses = new Dictionary<string, float>();
        
        public override float Progress
        {
            get
            {
                return GetTotalProgress();
            }
        }

        public override bool IsFinish
        {
            get
            {
                if (m_LoadFinishResult.Count == 0)
                {
                    return true;
                }

                return m_LoadFinishResult.All(r => r.Value != false);
            }
        }

        public override bool IsSuccess
        {
            get
            {
                if (m_LoadFinishResult.Count == 0)
                {
                    return false;
                }

                return m_LoadStateResult.All(r => r.Value != false);
            }
        }

        public string[] GetFailureScene()
        {
            return m_LoadStateResult.Where(kv=>kv.Value == false).Select(kv => kv.Key).ToArray();
        }
        
        public string[] GetSuccessfulScene()
        {
            return m_LoadStateResult.Where(kv=>kv.Value == true).Select(kv => kv.Key).ToArray();
        }
        
        public float GetTotalProgress()
        {
            if (m_LoadProgresses.Count == 0) return 1;
            return m_LoadProgresses.Sum(r => r.Value) / m_LoadProgresses.Count;
        }
        
        public override void Clear()
        {
            base.Clear();
            m_TotulFinish = false;
            m_LoadFinishResult.Clear();
            m_LoadProgresses.Clear();
            m_LoadStateResult.Clear();
        }
        
        #region Lifecycle
        protected override void OnEnter(IFsm<TOwner> fsm)
        {
            
            m_TotulFinish = false;
            var eventCom = LSEntry.Event;
            var sceneCom = LSEntry.Scene;
            eventCom.Subscribe(LoadSceneSuccessEventArgs.EventId, OnLoadSceneSuccess);
            eventCom.Subscribe(LoadSceneFailureEventArgs.EventId, OnLoadSceneFailure);
            eventCom.Subscribe(LoadSceneUpdateEventArgs.EventId, OnLoadSceneUpdate);
            eventCom.Subscribe(LoadSceneDependencyAssetEventArgs.EventId, OnLoadSceneDependencyAsset);

            fsm.Owner.BeforeLoad();
            
            // 卸载所有场景
            if (fsm.Owner.UnloadCurrentScenes)
            {
                string[] loadedSceneAssetNames = sceneCom.GetLoadedSceneAssetNames();
                
                for (int i = 0; i < loadedSceneAssetNames.Length; i++)
                {
                    if (!sceneCom.SceneIsUnloading(loadedSceneAssetNames[i]))
                    {
                        sceneCom.UnloadScene(loadedSceneAssetNames[i]);
                    }
                }
            }

            string[] scenes = fsm.Owner.SceneAssetNames;
            //根据数据表信息加载所有场景
            for (int i = 0; i < scenes.Length; i++)
            {
                string file = LSPath.FormatScenesUnityAsset(scenes[i]);
                m_LoadFinishResult[file] = false;
                m_LoadStateResult[file] = false;
                m_LoadProgresses[file] = 0;
                sceneCom.LoadScene(file, Constant.AssetPriority.SceneAsset, this);
            }
        }

        protected override void OnUpdate(IFsm<TOwner> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
            if (!this.IsFinish && !m_TotulFinish)
            {
                fsm.Owner.UpdateProgress(GetTotalProgress());
            }
            
            if (this.IsFinish && !m_TotulFinish)
            {
                var owner = fsm.Owner;
                m_TotulFinish = true;
                owner.AfterLoad();
                ChangeToNextState(fsm);
                owner.LoadFinish();
            }
        }

        protected override void OnLeave(IFsm<TOwner> procedureOwner, bool isShutdown)
        {
            var eventCom = LSEntry.Event;
            m_LoadFinishResult.Clear();
            m_LoadProgresses.Clear();
            m_LoadStateResult.Clear();
            eventCom.Unsubscribe(LoadSceneSuccessEventArgs.EventId, OnLoadSceneSuccess);
            eventCom.Unsubscribe(LoadSceneFailureEventArgs.EventId, OnLoadSceneFailure);
            eventCom.Unsubscribe(LoadSceneUpdateEventArgs.EventId, OnLoadSceneUpdate);
            eventCom.Unsubscribe(LoadSceneDependencyAssetEventArgs.EventId, OnLoadSceneDependencyAsset);

            base.OnLeave(procedureOwner, isShutdown);
        }
        #endregion

        #region EventHandlers
        private void OnLoadSceneSuccess(object sender, GameEventArgs e)
        {
            LoadSceneSuccessEventArgs ne = (LoadSceneSuccessEventArgs)e;
            if (ne.UserData != this)
            {
                return;
            }

            Log.Info("Load scene '{0}' OK.", ne.SceneAssetName);
            m_LoadFinishResult[ne.SceneAssetName] = true;
            m_LoadStateResult[ne.SceneAssetName] = true;
            m_LoadProgresses[ne.SceneAssetName] = 1;
        }

        private void OnLoadSceneFailure(object sender, GameEventArgs e)
        {
            LoadSceneFailureEventArgs ne = (LoadSceneFailureEventArgs)e;
            if (ne.UserData != this)
            {
                return;
            }

            Log.Error("Load scene '{0}' failure, error message '{1}'.", ne.SceneAssetName, ne.ErrorMessage);
            m_LoadFinishResult[ne.SceneAssetName] = true;
            m_LoadStateResult[ne.SceneAssetName] = false;
            m_LoadProgresses[ne.SceneAssetName] = 1;
        }

        private void OnLoadSceneUpdate(object sender, GameEventArgs e)
        {
            LoadSceneUpdateEventArgs ne = (LoadSceneUpdateEventArgs)e;
            m_LoadProgresses[ne.SceneAssetName] = ne.Progress;
            Log.Info("Load scene '{0}' update, progress '{1}'.", ne.SceneAssetName, ne.Progress.ToString("P2"));
        }

        private void OnLoadSceneDependencyAsset(object sender, GameEventArgs e)
        {
            LoadSceneDependencyAssetEventArgs ne = (LoadSceneDependencyAssetEventArgs)e;
            if (ne.UserData != this)
            {
                return;
            }

            Log.Info("Load scene '{0}' dependency asset '{1}', count '{2}/{3}'.", ne.SceneAssetName, ne.DependencyAssetName, ne.LoadedCount.ToString(), ne.TotalCount.ToString());
        }
        #endregion

    }
}